package com.virgo.redis.service;

import com.virgo.redis.config.CacheConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class RedisService {

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Autowired
	private CacheConfig cacheConfig;

	/**
	 * 给redis的key加一个前缀
	 * @param key 原始的key
	 * @return 新的key
	 */
	private String getKey(String key){
		String keyPerFix = cacheConfig.getRedisKey();
		if (!ObjectUtils.isEmpty(keyPerFix)) {
			return keyPerFix + key;
		}
		return key;
	}

	/**
	 * 实现命令：TTL key，以秒为单位，返回给定 key的剩余生存时间(TTL, time to live)。
	 * @param key 键
	 * @return 剩余有效期、单位：秒
	 */
	public Long ttl(String key) {
		key = getKey(key);
		return redisTemplate.getExpire(key);
	}

	/**
	 * 实现命令：expire 设置过期时间，单位秒
	 * @param key 键
	 */
	public void expire(String key, long timeout) {
		key = getKey(key);
		redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
	}

	/**
	 * 实现命令：SET key value，设置一个key-value（将字符串值 value关联到 key）
	 * @param key 参数
	 * @param value 值
	 */
	public void set(String key, String value) {
		key = getKey(key);
		redisTemplate.opsForValue().set(key, value);
	}

	/**
	 * 实现命令：SET key value，设置一个key-value（将字符串值 value关联到 key） 并设置有效期
	 * @param key 参数
	 * @param value 值
	 * @param expireSecond 有效期，单位为秒
	 */
	public void set(String key,String value,long expireSecond) {
		key = getKey(key);
		redisTemplate.opsForValue().set(key,value,expireSecond,TimeUnit.SECONDS);
	}

	/**
	 * 实现命令：setnx set not exist
	 * @param key 参数
	 * @param value 值
	 * @param expireSecond 有效期，单位为秒
	 */
	public Boolean setIfAbsent(String key,String value,long expireSecond) {
		key = getKey(key);
		return redisTemplate.opsForValue().setIfAbsent(key,value,expireSecond,TimeUnit.SECONDS);
	}

	/**
	 * 实现命令：GET key，返回 key所关联的字符串值。
	 * @param key 参数
	 * @return value
	 */
	public String get(String key) {
		key = getKey(key);
		return redisTemplate.opsForValue().get(key);
	}

	/**
	 * 实现命令：INCR key，增加key一次
	 * @param key 键
	 * @return 返回key执行加一操作的值
	 */
	public Long incr(String key) {
		key = getKey(key);
		return redisTemplate.opsForValue().increment(key);
	}

	/**
	 * 实现命令：decr key，减少key一次
	 * @param key 键
	 * @return 返回key执行减一操作的值
	 */
	public Long decr(String key) {
		key = getKey(key);
		return redisTemplate.opsForValue().decrement(key);
	}

	/**
	 * 实现命令：INCR key，增加key delta
	 * @param key 键
	 * @return 返回key执行加操作的值
	 */
	public Long incr(String key,long delta) {
		key = getKey(key);
		return redisTemplate.opsForValue().increment(key,delta);
	}

	/**
	 * 实现命令：decr key，减少key delta
	 * @param key 键
	 * @return 返回key执行减操作的值
	 */
	public Long decr(String key,long delta) {
		key = getKey(key);
		return redisTemplate.opsForValue().decrement(key,delta);
	}

	/**
	 * 实现命令：DEL key，删除一个key
	 * @param key 键
	 */
	public void del(String key) {
		key = getKey(key);
		redisTemplate.delete(key);
	}

	/**
	 * 实现命令：HSET key field value，将哈希表 key中的域 field的值设为 value
	 * @param key key
	 * @param field 下标
	 * @param value 值
	 */
	public void hset(String key, String field, Object value) {
		key = getKey(key);
		redisTemplate.opsForHash().put(key, field, value);
	}

	/**
	 * 实现命令：HGET key field，返回哈希表 key中给定域 field的值
	 * @param key key
	 * @param field 下标
	 * @return 值
	 */
	public String hget(String key, String field) {
		key = getKey(key);
		return (String) redisTemplate.opsForHash().get(key, field);
	}

	/**
	 * 实现命令：HDEL key field [field ...]，删除哈希表 key 中的一个或多个指定域，不存在的域将被忽略。
	 * @param key key
	 * @param fields 要删除的域
	 */
	public void hdel(String key, Object... fields) {
		key = getKey(key);
		redisTemplate.opsForHash().delete(key, fields);
	}

	/**
	 * 实现命令：HGETALL key，返回哈希表 key中，所有的域和值。
	 * @param key key
	 * @return Map 结果集
	 */
	public Map<Object, Object> hgetall(String key) {
		key = getKey(key);
		return redisTemplate.opsForHash().entries(key);
	}

	/**
	 * 实现命令：LPOP key，移除并返回列表 key的头元素。 出队
	 * @param key key
	 * @return 列表key的头元素。
	 */
	public String lpop(String key) {
		key = getKey(key);
		return (String) redisTemplate.opsForList().leftPop(key);
	}

	/**
	 * 实现命令：RPUSH key value，将一个值 value插入到列表 key的表尾(最右边)。 入队
	 * @param key key
	 * @param value 值
	 * @return 执行 LPUSH命令后，列表的长度。
	 */
	public Long rpush(String key, String value) {
		key = getKey(key);
		return redisTemplate.opsForList().rightPush(key,value);
	}

	/**
	 * 添加geo数据信息
	 * @param key String
	 * @param point Point
	 * @param member String
	 */
	public void geoAdd(String key, Point point, String member) {
		key = getKey(key);
		redisTemplate.opsForGeo().add(key,point,member);
	}

	/**
	 * 从geo数据中删除元素
	 * @param key String
	 * @param member String
	 */
	public void geoDelete(String key, String member) {
		key = getKey(key);
		redisTemplate.opsForGeo().remove(key,member);
	}

	/**
	 * geo搜索
	 * @param key String
	 * @param circle Circle
	 * @return List<GeoResult<RedisGeoCommands.GeoLocation<String>>>
	 */
	public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> geoSearch(String key, Circle circle) {
		key = getKey(key);
		//定义GEO参数，设置返回结果包含内容
		RedisGeoCommands.GeoRadiusCommandArgs args =
				RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
						.includeDistance()  //包含距离
						.includeCoordinates() //包含坐标
						.sortAscending(); //升序
		GeoResults<RedisGeoCommands.GeoLocation<String>> result = redisTemplate.opsForGeo().radius(key, circle, args);
		if (ObjectUtils.isEmpty(result)) {
			return new ArrayList<>();
		}
		return result.getContent();
	}
}
